playerOnlineTimeWeb/嵌入式卡片看这里.txt
2025-04-19 16:39:48 +08:00

231 lines
7.3 KiB
Plaintext
Raw Permalink 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.

<!--下面全是嵌入式卡片的代码复制后找个位置直接粘贴就行个人建议body的最下面-->
<!--记得更改serverUrl地址端口后面建议别动-->
<div id="minecraft-card-container"></div>
<script>
// 感谢deepseek大爹
// ===== 配置 =====
const config = {
serverUrl: "http://localhost:60048/api/widget-data",
updateInterval: 5000,
cardWidth: "300px"
};
(function initCard() {
if(localStorage.getItem('mcCardClosed')) return;
const card = createCard();
document.getElementById('minecraft-card-container').appendChild(card);
setupDragAndClose(card);
fetchData(card).then(() => {
setInterval(() => fetchData(card), config.updateInterval);
});
setTimeout(() => card.style.display = 'block', 100);
})();
function createCard() {
const card = document.createElement('div');
card.id = 'mc-status-card';
card.style.cssText = `
position: fixed; width: ${config.cardWidth}; z-index: 99999;
right: 20px; bottom: 20px; border-radius: 12px; background: white;
box-shadow: 0 4px 20px rgba(0,0,0,0.15); font-family: 'Segoe UI', sans-serif;
overflow: hidden; transition: all 0.2s; display: none;
`;
card.innerHTML = `
<div class="card-header" style="cursor:move;background:linear-gradient(135deg,#6a11cb 0%,#2575fc 100%);
color:white;padding:12px 15px;position:relative;">
<span>服务器玩家状态</span>
<span class="close-btn" style="position:absolute;right:10px;top:10px;color:white;
opacity:0.8;cursor:pointer;transition:opacity 0.2s;">×</span>
</div>
<div class="card-body" style="padding:0;">
<div class="loading" style="padding:20px;text-align:center;">
<div style="width:20px;height:20px;margin:0 auto 10px;border:3px solid rgba(0,0,0,0.1);
border-radius:50%;border-top-color:#2575fc;animation:spin 1s linear infinite;"></div>
<p>连接服务器中...</p>
</div>
</div>
`;
return card;
}
async function fetchData(card) {
const body = card.querySelector('.card-body');
try {
// 方案1尝试直接请求如果同源
let data = await tryDirectFetch();
// 方案2如果失败则使用服务器端代理
if(!data) data = await tryServerProxy();
if(data) {
renderCard(body, data);
} else {
throw new Error('所有数据获取方式均失败');
}
} catch(error) {
body.innerHTML = `
<div style="padding:20px;text-align:center;">
<p style="color:#dc3545;">⚠️ 数据加载失败</p>
<small style="color:#6c757d;">${error.message}</small>
<button onclick="location.reload()" style="margin-top:10px;padding:5px 10px;
background:#f8f9fa;border:1px solid #ddd;border-radius:4px;cursor:pointer;">
重试
</button>
</div>
`;
console.error('数据获取失败:', error);
}
}
async function tryDirectFetch() {
try {
const response = await fetch(config.serverUrl, {
headers: { 'Accept': 'application/json' }
});
return await fetchWidgetData();
} catch {
return null;
}
}
async function fetchWidgetData() {
try {
const response = await fetch('http://localhost:60048/api/widget-data');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
console.log('获取到的数据:', data);
return data;
} catch (error) {
console.error('获取widget数据时出错:', error);
return {
onlineCount: 0,
whitelistPlayers: [],
topPlayers: [],
timestamp: Date.now()
};
}
}
async function tryServerProxy() {
try {
// 备用的暂时不用deepseek建议留着
const proxyUrl = 'http:// ' +
encodeURIComponent(config.serverUrl);
const response = await fetch(proxyUrl);
return await response.json();
} catch {
return null;
}
}
function renderCard(container, data) {
container.innerHTML = `
<div style="display:flex;align-items:center;padding:10px 15px;border-bottom:1px solid rgba(0,0,0,0.05);">
<span style="font-size:1.2rem;margin-right:12px;color:#2575fc;">👥</span>
<span>在线玩家</span>
<span style="margin-left:auto;background:#0d6efd;color:white;
padding:0.25em 0.6em;border-radius:50rem;font-size:0.75em;">
${data.onlineCount}
</span>
</div>
${data.topPlayers?.length ? `
<div style="padding:8px 15px;background:#f8f9fa;">
<small><span style="color:#ffc107">🏆</span> 时长前三</small>
</div>
${data.topPlayers.map(p => renderPlayer(p)).join('')}
` : ''}
<div style="padding:8px 15px;background:#f8f9fa;">
<small><span style="color:#2575fc">ⓘ</span> 在线玩家</small>
</div>
<div style="max-height:150px;overflow-y:auto;">
${data.whitelistPlayers?.length ?
data.whitelistPlayers.map(p => renderPlayer(p)).join('') :
'<div style="padding:15px;text-align:center;color:#6c757d;font-style:italic">无在线玩家</div>'
}
</div>
<div style="padding:8px;text-align:center;color:#6c757d;font-size:0.8em;">
更新: ${formatTime(data.timestamp)}
</div>
`;
}
function renderPlayer(player) {
return `
<div style="display:flex;align-items:center;padding:8px 15px;border-bottom:1px solid rgba(0,0,0,0.05);">
<div style="width:24px;height:24px;border-radius:50%;background:#e9ecef;margin-right:10px;
display:flex;align-items:center;justify-content:center;font-size:12px;color:#495057;">
${player.name.charAt(0).toUpperCase()}
</div>
<span style="flex-grow:1;">${player.name}</span>
<span style="font-size:0.9rem;color:#6c757d;">${player.time}</span>
</div>
`;
}
function setupDragAndClose(card) {
const header = card.querySelector('.card-header');
let isDragging = false, offsetX, offsetY;
header.addEventListener('mousedown', (e) => {
if(e.target.classList.contains('close-btn')) return;
isDragging = true;
offsetX = e.clientX - card.getBoundingClientRect().left;
offsetY = e.clientY - card.getBoundingClientRect().top;
card.style.cursor = 'grabbing';
});
document.addEventListener('mousemove', (e) => {
if(!isDragging) return;
card.style.left = (e.clientX - offsetX) + 'px';
card.style.top = (e.clientY - offsetY) + 'px';
card.style.right = 'auto';
card.style.bottom = 'auto';
});
document.addEventListener('mouseup', () => {
isDragging = false;
card.style.cursor = 'grab';
});
card.querySelector('.close-btn').addEventListener('click', () => {
card.style.display = 'none';
localStorage.setItem('mcCardClosed', 'true');
});
}
function formatTime(timestamp) {
const now = Date.now();
const diff = (now - timestamp) / 1000;
if(diff < 60) return '刚刚';
if(diff < 3600) return `${Math.floor(diff/60)}分钟前`;
return `${Math.floor(diff/3600)}小时前`;
}
document.head.insertAdjacentHTML('beforeend', `
<style>
@keyframes spin { to { transform: rotate(360deg); } }
#mc-status-card:hover { transform: translateY(-2px); box-shadow: 0 6px 25px rgba(0,0,0,0.2); }
</style>
`);
</script>