服务器列表分类过滤 + 搜索增强

- 添加分类过滤下拉框(对接/api/servers/?category=参数)
- 自动从服务器数据提取分类选项填充下拉框
- 搜索框也匹配分类字段
- 添加刷新按钮
- 切换分类时自动重新加载服务器列表

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Your Name
2026-05-22 10:11:15 +08:00
parent 2ec24371c5
commit 83547c0b2e
+25 -2
View File
@@ -10,7 +10,11 @@
<header class="bg-slate-900 border-b border-slate-800 px-6 py-3 flex items-center justify-between">
<div class="flex items-center gap-4"><button @click="sidebarOpen=!sidebarOpen" class="text-slate-400 hover:text-white transition"><svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/></svg></button><h1 class="text-white font-semibold">服务器列表</h1></div>
<div class="flex items-center gap-3">
<select id="categoryFilter" onchange="this.dataset.loaded='';loadServers()" class="px-3 py-1.5 bg-slate-800 border border-slate-700 rounded-lg text-white text-sm">
<option value="">全部分类</option>
</select>
<input id="searchInput" type="text" placeholder="搜索服务器..." class="px-3 py-1.5 bg-slate-800 border border-slate-700 rounded-lg text-white text-sm placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-brand w-48">
<button onclick="loadServers()" class="px-3 py-1.5 bg-slate-800 hover:bg-slate-700 text-white text-sm rounded-lg transition">刷新</button>
<button onclick="showAddServer()" class="px-3 py-1.5 bg-brand hover:bg-brand-dark text-white text-sm rounded-lg transition">+ 添加</button>
</div>
</header>
@@ -99,13 +103,24 @@
async function loadServers(){
try{
const res=await apiFetch(API+'/servers/');
const category=document.getElementById('categoryFilter').value;
const url=API+'/servers/'+(category?'?category='+encodeURIComponent(category):'');
const res=await apiFetch(url);
if(!res) return;
const data=await res.json();
const servers=data.items||data;
const total=data.total||servers.length;
_serversCache={};servers.forEach(s=>_serversCache[s.id]=s);
document.getElementById('serverCount').textContent=`${total}`;
// Populate category filter from server data (only on first load)
if(!document.getElementById('categoryFilter').dataset.loaded){
const categories=[...new Set(servers.map(s=>s.category).filter(Boolean))].sort();
const sel=document.getElementById('categoryFilter');
const currentVal=sel.value;
sel.innerHTML='<option value="">全部分类</option>'+categories.map(c=>`<option value="${esc(c)}" ${c===currentVal?'selected':''}>${esc(c)}</option>`).join('');
sel.dataset.loaded='1';
}
const tbody=document.getElementById('serversTbody');
if(!servers.length){tbody.innerHTML='<tr><td colspan="7" class="px-4 py-8 text-center text-slate-500">暂无服务器</td></tr>';return}
tbody.innerHTML=servers.map(s=>`<tr class="hover:bg-slate-800/30 transition cursor-pointer ${selectedServerId===s.id?'bg-brand/5':''}" onclick="selectServer(${s.id})" id="row-${s.id}">
@@ -220,7 +235,15 @@
function esc(s){if(!s)return'';const d=document.createElement('div');d.textContent=s;return d.innerHTML}
function fmtTime(t){if(!t)return'--';return new Date(t+'Z').toLocaleString('zh-CN',{month:'short',day:'numeric',hour:'2-digit',minute:'2-digit'})}
document.getElementById('searchInput').addEventListener('input',()=>{const q=document.getElementById('searchInput').value.toLowerCase();document.querySelectorAll('#serversTbody tr').forEach(r=>{const name=r.querySelector('td:nth-child(2)')?.textContent.toLowerCase()||'';const addr=r.querySelector('td:nth-child(3)')?.textContent.toLowerCase()||'';r.style.display=(!q||name.includes(q)||addr.includes(q))?'':'none'})});
document.getElementById('searchInput').addEventListener('input',()=>{
const q=document.getElementById('searchInput').value.toLowerCase();
document.querySelectorAll('#serversTbody tr').forEach(r=>{
const name=r.querySelector('td:nth-child(2)')?.textContent.toLowerCase()||'';
const addr=r.querySelector('td:nth-child(3)')?.textContent.toLowerCase()||'';
const cat=r.querySelector('td:nth-child(4)')?.textContent.toLowerCase()||'';
r.style.display=(!q||name.includes(q)||addr.includes(q)||cat.includes(q))?'':'none';
});
});
loadServers();
</script>