1
<!DOCTYPE html> <html lang="zh-CN"> <head>
<meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>服务器详情</title> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700&display=swap"> <script src="https://html2canvas.hertzen.com/dist/html2canvas.min.js"></script> <style> /* ========== 以下样式与原文件完全一致,无任何修改 ========== */ * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Noto Sans SC', 'Segoe UI', 'Microsoft YaHei', sans-serif; } body { background: linear-gradient(135deg, #0f1a3d, #1c2e50); color: #333; min-height: 100vh; padding: 20px; display: flex; flex-direction: column; align-items: center; } .container { width: 100%; max-width: 1200px; background: white; border-radius: 20px; box-shadow: 0 15px 40px rgba(0, 0, 0, 0.5); overflow: hidden; margin: 20px 0; position: relative; } header { background: linear-gradient(90deg, #1a5e1f, #2e7d32); color: white; padding: 30px 40px; text-align: center; position: relative; overflow: hidden; } header::before { content: ""; position: absolute; top: -50%; left: -50%; width: 200%; height: 200%; background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, transparent 70%); transform: rotate(30deg); } h1 { font-size: 2.8rem; margin-bottom: 10px; letter-spacing: 1px; text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3); position: relative; font-weight: 700; } .subtitle { font-size: 1.2rem; opacity: 0.9; margin-top: 10px; position: relative; font-weight: 400; } .content { padding: 30px 40px; } .controls { display: flex; justify-content: space-between; margin-bottom: 20px; padding: 15px 20px; background: #f8f9fa; border-radius: 15px; box-shadow: 0 4px 12px rgba(0,0,0,0.05); flex-wrap: wrap; gap: 15px; } .controls-right { display: flex; align-items: center; gap: 15px; } .server-id { position: absolute; top: 50%; transform: translateY(-10px); left: 10px; background: #2e7d32; color: white; width: 30px; height: 30px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold; font-size: 0.9rem; z-index: 1; } .server-card.closed .server-id { background: #e74c3c; } .server-card.today-closed .server-id { background: #f39c12; } .server-card.upcoming .server-id { background: #9b59b6; } .sort-controls { display: flex; gap: 15px; align-items: center; flex-wrap: wrap; } .sort-label { font-weight: 500; color: #555; } .sort-select { padding: 10px 20px; border-radius: 50px; border: 2px solid #e0e0e0; background: white; font-size: 1rem; cursor: pointer; outline: none; transition: all 0.3s ease; min-width: 180px; } .sort-select:focus { border-color: #2e7d32; box-shadow: 0 0 0 3px rgba(46, 125, 50, 0.2); } .download-all-btn { background: linear-gradient(90deg, #3498db, #2980b9); color: white; border: none; padding: 12px 30px; font-size: 1.1rem; border-radius: 50px; cursor: pointer; transition: all 0.3s ease; display: flex; align-items: center; gap: 10px; box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); font-weight: 500; } .download-all-btn:hover { transform: translateY(-3px); box-shadow: 0 8px 20px rgba(0, 0, 0, 0.3); background: linear-gradient(90deg, #2980b9, #2573a7); } .server-card { background: white; border-radius: 15px; box-shadow: 0 5px 20px rgba(0, 0, 0, 0.08); padding: 25px; margin-bottom: 30px; border-left: 5px solid #2e7d32; transition: all 0.3s ease; position: relative; overflow: hidden; } .server-card.closed { border-left: 5px solid #e74c3c; } .server-card.today-closed { border-left: 5px solid #f39c12; } .server-card.upcoming { border-left: 5px solid #9b59b6; } .server-card:hover { transform: translateY(-5px); box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15); } .server-card::before { content: ; position: absolute; top: 0; left: 0; width: 100%; height: 5px; background: linear-gradient(90deg, #2e7d32, #1a5e1f); } .server-card.closed::before { background: linear-gradient(90deg, #e74c3c, #c0392b); } .server-card.today-closed::before { background: linear-gradient(90deg, #f39c12, #e67e22); } .server-card.upcoming::before { background: linear-gradient(90deg, #9b59b6, #8e44ad); } .server-title { color: #2e7d32; font-size: 1.8rem; margin-bottom: 15px; display: flex; justify-content: space-between; align-items: flex-start; position: relative; font-weight: 600; }
.server-title-content {
display: flex;
align-items: flex-start;
gap: 15px;
flex: 1;
}
.server-name-container {
display: flex;
flex-direction: row;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
.chinese-name {
display: block;
font-size: 1.8rem;
color: inherit;
}
.english-name {
display: block;
font-size: 1.1rem;
color: #7f8c8d;
font-weight: 400;
margin-top: 1px;
line-height: 0;
width: 100%;
}
.server-card.closed .server-title {
color: #e74c3c;
}
.server-card.today-closed .server-title {
color: #f39c12;
}
.server-card.upcoming .server-title {
color: #9b59b6;
}
.server-title i {
background: #e8f5e9;
width: 50px;
height: 50px;
border-radius: 50%;
display: flex !important;
align-items: center !important;
justify-content: center !important;
font-size: 1.3rem;
line-height: 1 !important;
}
.server-card.closed .server-title i {
background: #ffebee;
}
.server-card.today-closed .server-title i {
background: #fef5e7;
}
.server-card.upcoming .server-title i {
background: #f5e9ff;
}
.server-info {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin-bottom: 20px;
}
.info-item {
display: flex;
flex-direction: column;
}
.info-label {
font-weight: 600;
color: #7f8c8d;
font-size: 0.95rem;
margin-bottom: 5px;
display: flex;
align-items: center;
gap: 8px;
}
.info-value {
font-size: 1.15rem;
color: #2c3e50;
font-weight: 500;
word-break: break-all;
}
.ip-address {
font-family: monospace;
background: #f8f9fa;
padding: 10px 15px;
border-radius: 8px;
border: 1px solid #e0e0e0;
color: #2e7d32;
font-weight: 600;
font-size: 1.1rem;
}
.server-card.closed .ip-address {
color: #e74c3c;
}
.server-card.today-closed .ip-address {
color: #f39c12;
}
.server-card.upcoming .ip-address {
color: #9b59b6;
}
.description {
background: #f8f9fa;
border-left: 3px solid #3498db;
padding: 18px;
border-radius: 0 12px 12px 0;
margin: 25px 0 15px;
font-size: 1.1rem;
line-height: 1.7;
}
.server-status {
position: absolute;
top: 25px;
right: 25px;
padding: 8px 20px;
border-radius: 30px;
font-weight: bold;
font-size: 0.95rem;
text-transform: uppercase;
letter-spacing: 1px;
box-shadow: 0 3px 8px rgba(0,0,0,0.1);
}
.status-open {
background: #e8f5e9;
color: #2e7d32;
border: 1px solid #a5d6a7;
}
.status-closed {
background: #ffebee;
color: #e74c3c;
border: 1px solid #ef9a9a;
}
.status-today-closed {
background: #fef5e7;
color: #f39c12;
border: 1px solid #f8c471;
}
.status-upcoming {
background: #f5e9ff;
color: #9b59b6;
border: 1px solid #d6b8f1;
}
.special-tag {
background: #f39c12;
color: white;
padding: 4px 12px;
border-radius: 30px;
font-size: 0.9rem;
display: inline-block;
font-weight: 500;
margin-left: 0;
}
.server-card.upcoming .special-tag {
background: #9b59b6;
}
.footer {
background: #1c2e50;
color: white;
padding: 30px 40px;
text-align: center;
margin-top: 20px;
width: 100%;
}
.footer-content {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
gap: 20px;
width: 100%;
}
.footer-section {
flex: 1;
min-width: 200px;
max-width: 350px;
}
.footer-title {
font-size: 1.3rem;
margin-bottom: 15px;
color: #ecf0f1;
font-weight: 500;
}
.footer-list {
list-style: none;
display: flex;
flex-direction: column;
flex-wrap: wrap;
max-height: 180px;
}
.footer-list li {
margin-bottom: 10px;
display: flex;
align-items: center;
gap: 10px;
font-size: 1rem;
min-width: 150px;
}
.maintainers-list {
display: grid;
grid-template-rows: repeat(3, auto);
grid-auto-flow: column;
gap: 10px 30px;
max-height: none;
}
.maintainers-list li {
margin-bottom: 0;
min-width: 150px;
}
.footer-bottom {
margin-top: 30px;
padding-top: 20px;
border-top: 1px solid rgba(255, 255, 255, 0.15);
font-size: 0.95rem;
opacity: 0.8;
width: 100%;
}
.stats-bar {
display: flex;
justify-content: space-around;
background: #f8f9fa;
padding: 18px;
border-radius: 15px;
margin: 25px 0;
border: 1px solid #e0e0e0;
box-shadow: 0 4px 12px rgba(0,0,0,0.05);
}
.stat-item {
text-align: center;
padding: 10px;
}
.stat-number {
font-size: 2.2rem;
font-weight: 700;
color: #2e7d32;
line-height: 1;
}
.closed-count {
color: #e74c3c;
}
.today-closed-count {
color: #f39c12;
}
.upcoming-count {
color: #9b59b6;
}
.stat-label {
font-size: 1rem;
color: #7f8c8d;
font-weight: 500;
}
.filter-controls {
display: flex;
justify-content: flex-start;
align-items: center;
gap: 15px;
margin-bottom: 20px;
flex-wrap: wrap;
padding: 15px;
background: #f8f9fa;
border-radius: 15px;
box-shadow: 0 4px 12px rgba(0,0,0,0.05);
position: relative;
}
.filter-label {
font-weight: 500;
color: #555;
white-space: nowrap;
}
.settings-btn {
background: #f8f9fa;
border: 2px solid #e0e0e0;
border-radius: 50%;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s ease;
}
.settings-btn:hover {
background: #e0e0e0;
transform: rotate(30deg);
}
.language-toggle {
display: flex;
background: #e8f5e9;
border-radius: 50px;
overflow: hidden;
margin-top: 20px;
border: 2px solid #e0e0e0;
}
.lang-btn {
padding: 10px 25px;
border: none;
background: transparent;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
font-size: 1rem;
flex: 1;
text-align: center;
}
.lang-btn.active {
background: #2e7d32;
color: white;
}
.download-progress {
display: none;
text-align: center;
padding: 15px;
background: #e8f5e9;
border-radius: 10px;
margin-top: 20px;
z-index: 1001;
}
.progress-bar {
height: 8px;
background: #e0e0e0;
border-radius: 4px;
margin-top: 10px;
overflow: hidden;
}
.progress-fill {
height: 100%;
background: #2e7d32;
width: 0%;
transition: width 0.3s ease;
}
.header-icons {
display: flex;
justify-content: center;
gap: 20px;
margin-top: 15px;
}
.icon-circle {
width: 65px;
height: 65px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.2);
display: flex;
align-items: center;
justify-content: center;
font-size: 2rem;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
}
.download-modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.7);
z-index: 1000;
justify-content: center;
align-items: center;
}
.modal-content {
background: white;
border-radius: 20px;
width: 90%;
max-width: 500px;
overflow: hidden;
box-shadow: 0 25px 50px rgba(0,0,0,0.3);
transform: translateY(20px);
opacity: 0;
transition: all 0.4s ease;
}
.modal-header {
background: linear-gradient(90deg, #3498db, #2980b9);
color: white;
padding: 25px;
text-align: center;
position: relative;
}
.modal-title {
font-size: 1.8rem;
font-weight: 600;
}
.modal-body {
padding: 30px;
}
.res-options {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20px;
margin-bottom: 30px;
}
.res-option {
border: 2px solid #e0e0e0;
border-radius: 15px;
padding: 20px;
text-align: center;
cursor: pointer;
transition: all 0.3s ease;
}
.res-option:hover {
border-color: #3498db;
box-shadow: 0 5px 15px rgba(52, 152, 219, 0.2);
transform: translateY(-5px);
}
.res-option.selected {
border-color: #3498db;
background: #e8f4fc;
}
.res-percent {
font-size: 1.8rem;
font-weight: 700;
color: #3498db;
margin-bottom: 5px;
}
.res-label {
font-size: 1rem;
color: #7f8c8d;
}
.modal-footer {
display: flex;
justify-content: flex-end;
padding: 20px 30px;
background: #f8f9fa;
border-top: 1px solid #e0e0e0;
}
.modal-btn {
padding: 12px 30px;
border-radius: 50px;
border: none;
font-weight: 500;
font-size: 1.1rem;
cursor: pointer;
transition: all 0.3s ease;
}
.cancel-btn {
background: #f8f9fa;
color: #7f8c8d;
margin-right: 15px;
}
.cancel-btn:hover {
background: #e0e0e0;
}
.download-btn {
background: linear-gradient(90deg, #2ecc71, #27ae60);
color: white;
box-shadow: 0 5px 15px rgba(46, 204, 113, 0.3);
}
.download-btn:hover {
background: linear-gradient(90deg, #27ae60, #219653);
transform: translateY(-3px);
box-shadow: 0 8px 20px rgba(46, 204, 113, 0.4);
}
.sort-direction {
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
border-radius: 50%;
background: #e8f5e9;
border: 2px solid #e0e0e0;
cursor: pointer;
transition: all 0.3s ease;
}
.sort-direction:hover {
background: #d5e8d6;
}
.sort-direction.desc i {
transform: rotate(180deg);
}
.sort-feedback {
position: fixed;
top: 20px;
left: 50%;
transform: translateX(-50%);
color: white;
padding: 12px 25px;
border-radius: 30px;
font-weight: 500;
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
z-index: 3000;
opacity: 0;
transition: all 0.3s ease;
background: rgba(0,0,0,0.7);
backdrop-filter: blur(5px);
}
.sort-feedback.show {
opacity: 1;
transform: translateX(-50%) translateY(10px);
}
.settings-modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.5);
z-index: 1000;
backdrop-filter: blur(5px);
}
.settings-content {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: white;
border-radius: 20px;
padding: 30px;
width: 90%;
max-width: 500px;
box-shadow: 0 25px 50px rgba(0,0,0,0.3);
}
.settings-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 25px;
padding-bottom: 15px;
border-bottom: 2px solid #f0f0f0;
}
.settings-title {
font-size: 1.5rem;
font-weight: 700;
color: #333;
}
.close-settings {
background: none;
border: none;
font-size: 1.5rem;
cursor: pointer;
color: #666;
padding: 5px;
border-radius: 50%;
transition: all 0.3s ease;
}
.close-settings:hover {
background: #f0f0f0;
color: #333;
}
.settings-section {
margin-bottom: 25px;
}
.settings-section h3 {
margin-bottom: 15px;
color: #555;
font-weight: 600;
}
.setting-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 0;
border-bottom: 1px solid #f8f8f8;
}
.setting-label {
font-weight: 500;
color: #333;
}
.toggle-switch {
width: 50px;
height: 26px;
background: #e0e0e0;
border-radius: 13px;
position: relative;
cursor: pointer;
transition: all 0.3s ease;
}
.toggle-switch::before {
content: ;
position: absolute;
width: 22px;
height: 22px;
background: white;
border-radius: 50%;
top: 2px;
left: 2px;
transition: all 0.3s ease;
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
}
.toggle-switch.active {
background: #2e7d32;
}
.toggle-switch.active::before {
left: 26px;
}
.sort-center-wrapper {
position: absolute;
left: 50%;
transform: translateX(-50%);
display: flex;
align-items: center;
gap: 10px;
}
.download-wrapper {
margin-left: auto;
}
@media (max-width: 900px) {
.controls {
flex-direction: column;
gap: 20px;
}
.sort-center-wrapper {
position: static;
transform: none;
margin: 10px 0;
}
.download-wrapper {
margin-left: 0;
width: 100%;
}
.download-all-btn {
width: 100%;
justify-content: center;
}
.footer-list {
display: block;
max-height: none;
}
.footer-list li {
margin-bottom: 10px;
}
.maintainers-list {
display: block;
}
.maintainers-list li {
margin-bottom: 10px;
}
}
@media (max-width: 768px) {
.filter-controls {
flex-direction: column;
align-items: stretch;
}
.sort-center-wrapper {
flex-wrap: wrap;
justify-content: center;
}
h1 {
font-size: 2.2rem;
}
.content {
padding: 20px;
}
.server-info {
grid-template-columns: 1fr;
}
.server-title {
font-size: 1.5rem;
}
.server-status {
position: static;
margin-top: 10px;
display: inline-block;
}
.stats-bar {
flex-direction: column;
gap: 15px;
}
}
</style>
</head> <body class="chinese">
<header>
服务器详情
</header>
发起人:
<select class="sort-select" id="founder-select">
<option value="all">全部</option>
</select>
排序方式:
<select class="sort-select" id="sort-select">
<option value="date">开服日期 (默认)</option>
<option value="name">服务器名称</option>
<option value="founder">发起人</option>
<option value="size">地图尺寸</option>
<option value="group">所属群聊</option>
<option value="version">版本号</option>
<option value="open-time">开放时间</option>
</select>
<button class="download-all-btn" id="download-btn">
下载全图
</button>
设置
<button class="close-settings" id="closeSettings">
</button>
显示设置
隐藏已关闭服务器
隐藏即将开启服务器
关于
服务器详情-v12.0
更新时间: 2025.12.08
制作人员: Hank
<script>
// ========== 全局变量 ==========
let servers = [];
let siteConfig = {};
let sortOrder = 'desc';
// 工具函数
function parseServerId(id) {
const parts = id.split('-');
const serverNumber = parseInt(parts[0]);
const founderBlocks = parts.slice(1).join('-').split('&');
const founders = founderBlocks.map(block => {
const [founderPart, countryPart] = block.split('-');
const [founderName, founderServerId] = founderPart.split('_');
const [countryCode, countryServerId] = countryPart.split('_');
return {
name: founderName,
founderServerId: parseInt(founderServerId),
countryCode,
countryServerId: parseInt(countryServerId)
};
});
return { serverNumber, founders, isJoint: founders.length > 1,
primaryFounder: founders[0].name,
allFounderNames: founders.map(f => f.name).join('&') };
}
function parseDate(dateStr) {
if (dateStr.includes('-')) {
const parts = dateStr.split('-');
return new Date(parts[0], parts[1] - 1, parts[2]);
} else if (dateStr.includes('.')) {
const parts = dateStr.split('.');
return new Date(parts[0], parts[1] - 1, parts[2]);
}
return new Date(0);
}
function showSortFeedback(message) {
const feedback = document.getElementById('sortFeedback');
if (!feedback) return;
feedback.textContent = message;
feedback.classList.add('show');
setTimeout(() => {
feedback.classList.remove('show');
}, 3000);
}
// 生成服务器卡片
function generateServerCard(server) {
const today = new Date();
const dayOfWeek = today.getDay();
let statusClass = "";
let statusText = "";
let iconClass = "train";
// 解析服务器ID(如果需要ID信息,这里仍保留解析)
const idInfo = parseServerId(server.id);
// 根据状态显示
if (server.status === "upcoming") {
statusClass = "upcoming";
statusText = "即将开启";
iconClass = "clock";
} else if (server.openTime[0] === 0) {
statusClass = "closed";
statusText = "关闭";
iconClass = "lock";
} else if (server.openTime[0] === 7) {
statusClass = "";
statusText = "开放";
iconClass = "train";
} else {
if (server.openTime.includes(dayOfWeek)) {
statusClass = "";
statusText = "开放";
iconClass = "train";
} else {
statusClass = "today-closed";
statusText = "今日关闭";
iconClass = "calendar-times";
}
}
// 构建发起人显示(支持多个)
const foundersHtml = Array.isArray(server.founder)
? server.founder.join('&')
: server.founder;
// 构建标签HTML
let tagsHtml = "";
server.tags.forEach(tag => {
tagsHtml += `${tag}`;
});
// 构建开放时间文本
let openTimeText = "";
if (server.status === "upcoming") {
openTimeText = "即将开启";
} else if (server.openTime[0] === 0) {
openTimeText = "以实际为准";
} else if (server.openTime[0] === 7) {
openTimeText = "长期开放";
} else {
const dayNames = ["周日", "周一", "周二", "周三", "周四", "周五", "周六"];
openTimeText = "每周" + server.openTime.map(day => dayNames[day]).join("、");
}
return `
${server.chineseName}
${tagsHtml}
${server.englishName}
${server.chineseName} ${tagsHtml} ${server.englishName}
发起人 ${foundersHtml}
开服日期 ${server.date}
开放时间 ${openTimeText}
地图尺寸 ${server.size}
版本 ${server.version}
所属群聊 ${server.group}
IP地址 ${server.ip}
简介: ${server.description}
`;
}
// 渲染服务器列表
function renderServerList() {
const container = document.querySelector('.servers-container');
const upcoming = servers.filter(s => s.status === 'upcoming');
const others = servers.filter(s => s.status !== 'upcoming');
container.innerHTML = [...upcoming, ...others].map(s => generateServerCard(s)).join();
updateStats();
}
function updateStats() {
const today = new Date();
const dayOfWeek = today.getDay();
let openCount = 0, closedCount = 0, upcomingCount = 0;
servers.forEach(s => {
if (s.status === 'upcoming') upcomingCount++;
else if (s.openTime[0] === 0) closedCount++;
else if (s.openTime[0] === 7) openCount++;
else if (s.openTime.includes(dayOfWeek)) openCount++;
});
const maxId = servers.length ? Math.max(...servers.map(s => parseServerId(s.id).serverNumber)) : 0;
document.getElementById('total-count').textContent = maxId;
document.getElementById('configured-count').textContent = openCount + upcomingCount;
document.getElementById('open-count').textContent = openCount;
document.getElementById('closed-count').textContent = closedCount;
document.getElementById('upcoming-count').textContent = upcomingCount;
}
// 页脚配置更新
function updateFooter(config) {
// 文档信息列表
const infoList = document.getElementById('footer-info');
if (infoList && config.doc_info) {
infoList.innerHTML = config.doc_info.map(item =>
`
` ).join(); } // 维护团队列表 const maintList = document.getElementById('footer-maintainers'); if (maintList && config.maintainers) { maintList.innerHTML = config.maintainers.map(name => `
` ).join(); } // 注意事项列表 const notesList = document.getElementById('footer-notes'); if (notesList && config.notes) { notesList.innerHTML = config.notes.map(note => `
` ).join(); } // 页脚底部文字 if (config.footer_bottom) { document.getElementById('footer-bottom').innerHTML = `
${config.footer_bottom}
`;
}
// 关于页面内的信息
if (config.about) {
document.getElementById('settings-about').innerHTML = config.about;
}
}
// ========== 排序与筛选 ==========
function parseMapSize(size) {
if (!size || size === '未知' || size === '待定' || size === '0x0') return -1;
const matches = size.match(/(\d+)\s*[x×]\s*(\d+)/);
if (matches && matches.length >= 3) {
return parseInt(matches[1]) * parseInt(matches[2]);
}
const single = size.match(/\d+/);
return single ? parseInt(single[0]) : -1;
}
function filterAndSortServers() {
const selectedFounder = document.getElementById('founder-select').value;
const hideClosed = document.getElementById('hideClosedToggle').classList.contains('active');
const hideUpcoming = document.getElementById('hideUpcomingToggle').classList.contains('active');
const sortBy = document.getElementById('sort-select').value;
let filtered = servers.filter(server => {
const founders = Array.isArray(server.founder)
? server.founder
: server.founder.split('&').map(s => s.trim());
if (selectedFounder !== 'all' && !founders.includes(selectedFounder)) return false;
if (hideClosed && server.openTime[0] === 0 && server.status !== 'upcoming') return false;
if (hideUpcoming && server.status === 'upcoming') return false;
return true;
});
filtered.sort((a, b) => {
if (a.status === 'upcoming' && b.status !== 'upcoming') return -1;
if (a.status !== 'upcoming' && b.status === 'upcoming') return 1;
if (a.status === 'upcoming' && b.status === 'upcoming') return parseDate(a.date) - parseDate(b.date);
const dir = sortOrder === 'asc' ? 1 : -1;
let av, bv;
switch (sortBy) {
case 'name':
av = a.chineseName; bv = b.chineseName;
return dir * av.localeCompare(bv);
case 'founder':
av = (Array.isArray(a.founder) ? a.founder[0] : a.founder.split('&')[0].trim());
bv = (Array.isArray(b.founder) ? b.founder[0] : b.founder.split('&')[0].trim());
return dir * av.localeCompare(bv);
case 'date':
return dir * (parseDate(a.date) - parseDate(b.date));
case 'size':
av = parseMapSize(a.size); bv = parseMapSize(b.size);
if (av === -1 && bv === -1) return 0;
if (av === -1) return dir * 1;
if (bv === -1) return dir * -1;
return dir * (av - bv);
case 'group':
return dir * a.group.localeCompare(b.group);
case 'version':
return dir * a.version.localeCompare(b.version);
case 'open-time':
return dir * (a.openTime.length - b.openTime.length);
default:
return dir * (parseDate(a.date) - parseDate(b.date));
}
});
document.querySelector('.servers-container').innerHTML = filtered.map(s => generateServerCard(s)).join();
updateStats();
}
// ========== 初始化:加载数据 ==========
document.addEventListener('DOMContentLoaded', function() {
fetch('api/get_servers.php')
.then(res => res.json())
.then(data => {
// data 应包含 servers 和 config
servers = data.servers;
siteConfig = data.config || {};
// 渲染页面
renderServerList();
filterAndSortServers();
updateFooter(siteConfig);
// 动态生成发起人下拉选项
const founderSelect = document.getElementById('founder-select');
founderSelect.querySelectorAll('option:not([value="all"])').forEach(opt => opt.remove());
if (data.founders && Array.isArray(data.founders)) {
data.founders.forEach(name => {
const option = document.createElement('option');
option.value = name;
option.textContent = name;
founderSelect.appendChild(option);
});
}
// 初始化下载分辨率选项
document.querySelector('.res-option[data-res="100"]').classList.add('selected');
})
.catch(err => {
console.error('数据加载失败:', err);
alert('无法获取数据,请稍后刷新。');
});
// 排序方向切换
document.getElementById('sort-direction').addEventListener('click', function() {
sortOrder = sortOrder === 'asc' ? 'desc' : 'asc';
this.classList.toggle('desc');
filterAndSortServers();
const sortText = document.getElementById('sort-select').selectedOptions[0].text;
showSortFeedback(`已按${sortText}${sortOrder === 'desc' ? '降序' : '升序'}排序`);
});
// 发起人、排序方式变化
document.getElementById('founder-select').addEventListener('change', function() {
filterAndSortServers();
showSortFeedback(`已筛选发起人:${this.selectedOptions[0].text}`);
});
document.getElementById('sort-select').addEventListener('change', function() {
filterAndSortServers();
const sortText = this.selectedOptions[0].text;
showSortFeedback(`已按${sortText}${sortOrder === 'desc' ? '降序' : '升序'}排序`);
});
// 设置模态框
const settingsBtn = document.getElementById('settings-btn');
const settingsModal = document.getElementById('settingsModal');
const closeSettings = document.getElementById('closeSettings');
settingsBtn.addEventListener('click', () => { settingsModal.style.display = 'flex'; });
closeSettings.addEventListener('click', () => { settingsModal.style.display = 'none'; });
settingsModal.addEventListener('click', (e) => { if (e.target === settingsModal) settingsModal.style.display = 'none'; });
// 隐藏/显示开关
document.getElementById('hideClosedToggle').addEventListener('click', function() {
this.classList.toggle('active');
filterAndSortServers();
});
document.getElementById('hideUpcomingToggle').addEventListener('click', function() {
this.classList.toggle('active');
filterAndSortServers();
});
// ---- 下载全图功能 ----
const downloadBtn = document.getElementById('download-btn');
const modal = document.getElementById('download-modal');
const modalContent = document.getElementById('modal-content');
const cancelBtn = document.getElementById('cancel-download');
const confirmBtn = document.getElementById('confirm-download');
const progressDiv = document.getElementById('download-progress');
const progressFill = document.getElementById('progress-fill');
let selectedRes = 100;
downloadBtn.addEventListener('click', () => {
modal.style.display = 'flex';
setTimeout(() => { modalContent.style.transform = 'translateY(0)'; modalContent.style.opacity = '1'; }, 10);
});
cancelBtn.addEventListener('click', () => {
modalContent.style.transform = 'translateY(20px)'; modalContent.style.opacity = '0';
setTimeout(() => { modal.style.display = 'none'; }, 300);
});
document.querySelectorAll('.res-option').forEach(opt => {
opt.addEventListener('click', function() {
document.querySelectorAll('.res-option').forEach(o => o.classList.remove('selected'));
this.classList.add('selected');
selectedRes = this.dataset.res;
});
});
confirmBtn.addEventListener('click', () => {
modalContent.style.transform = 'translateY(20px)'; modalContent.style.opacity = '0';
setTimeout(() => { modal.style.display = 'none'; }, 300);
progressDiv.style.display = 'block';
progressFill.style.width = '0%';
setTimeout(() => {
progressFill.style.width = '30%';
html2canvas(document.querySelector('.container'), {
scale: selectedRes / 100,
useCORS: true, logging: false, backgroundColor: null,
onclone: function(clonedDoc) { clonedDoc.querySelector('.container').style.boxShadow = 'none'; }
}).then(canvas => {
progressFill.style.width = '90%';
setTimeout(() => {
const link = document.createElement('a');
link.href = canvas.toDataURL('image/png');
link.download = `服务器详情_${selectedRes}%_${new Date().toISOString().slice(0,10)}.png`;
document.body.appendChild(link); link.click(); document.body.removeChild(link);
progressFill.style.width = '100%';
setTimeout(() => { progressDiv.style.display = 'none'; alert('图片下载完成!'); }, 500);
}, 500);
}).catch(err => {
console.error(err); progressDiv.style.display = 'none'; alert('图片生成失败: ' + err.message);
});
}, 500);
});
// 初始化分辨率默认选中
document.querySelector('.res-option[data-res="100"]').classList.add('selected');
});
</script>
</body> </html>